L.Room = L.Class.extend({
	includes: L.Mixin.Events,

	initialize: function(id, type, ownerId, background, couplingOptions){
		var self = this;
		self.id = id;
		self.type = type;
		self._ownerId = ownerId;
		self.background = background;
		this._objects = {};
		this._roomList = null;

		/* set History */
		var execute = function(cmd){
			self.execute.call(self, cmd);
		};
		var rollback = function(cmd){
			self.rollback.call(self, cmd);
		};
		self._history = new L.History(id, execute, rollback, couplingOptions);
		
		self._roomModerator = new L.RoomModerator(self, couplingOptions);

		this._fireEvents = true;
	},

	fireEvents: function(bool){
		this._fireEvents = bool;
	},

	addCommand: function(cmd_name,cmd_params){
		var command = {};
		switch(cmd_name) {
		case "addObject":
			command = {
				type: "stroke",
				stroke: cmd_params.object
		};
			break;
		case "removeObject":
			command = {
				type: "erase",
				strokeId: cmd_params.objectId
		};
			break;
		case "updateObject":
			var data = cmd_params.data;
			data.id = cmd_params.objectId;
			command = {
					type: "update",
					stroke: data
			};
			break;
		case "replaceObjects":
			var data = cmd_params.data;
			data.id = cmd_params.objectId;
			command = {
					type: "replace",
					stroke: data
			};
			break;
		case "changeStore":
			command = {
				type: "store",
				store: cmd_params.store
		};
			break;
		}
		this._history.addCommand(command);
	},

	/* History callbacks */
	execute: function(command){
		switch(command.type) {
		case "strokes":
		case "batch":
		case "summary":
			command.oldStrokes = this.changeStore(command.strokes);
			break;
		case "stroke":
			this.addObject(command.stroke);
			break;
		case "erase":
			command.stroke = this.getObject(command.strokeId);
			this.removeObject(command.strokeId);
			break;
		case "update":
			command.oldStroke = L.Util.extend({},this.getObject(command.stroke.id));
			this.updateObject(command.stroke.id, command.stroke);
			break;
		case "replace":
			command.oldStroke = this.getObject(command.stroke.id);
			this.replaceObject(command.stroke.id, command.stroke);
			break;
		case "store":
			command.oldStore = this.changeStore(command.store);
			break;      
		}
	},

	rollback: function(command){
		switch(command.type) {
		case "strokes":
		case "batch":
		case "summary":
			this.changeStore(command.oldStrokes);
			break;
		case "stroke":
			this.removeObject(command.stroke.id);
			break;
		case "erase":
			this.addObject(command.stroke);
			break;
		case "update":
		case "replace":
			this.replaceObject(command.oldStroke.id, command.oldStroke);  
			break;        
		case "store":
			this.changeStore(command.oldStore);
			break;
		}      
	},

	addObject: function(object) {
		var id = uniqueId(object);
		if ( this.hasObject(id) )
			console.log('[WARNING] L.Map.addObject sobreescribio un objeto');

		this._objects[id] = object;
		if (this._fireEvents) {
			this.fire('objectAction',{
				action_name:'addObject',
				attr_list:{'object':object}
			});
		}
	},

	removeObject: function(objectId){
		if (this._objects.hasOwnProperty(objectId)) 
			delete this._objects[objectId]; 
		else
			console.log('[WARNING] L.Map.removeObject intento eliminar un objeto que no existe');

		if (this._fireEvents) {
			this.fire('objectAction',{
				action_name:'removeObject',
				attr_list:{'objectId':objectId}
			});
		}    
	},

	updateObject: function(objectId, data) {
		var obj = this.getObject(objectId);
		for (var key in data) {
			if(data.hasOwnProperty(key))
				obj[key] = data[key];
		}

		if (this._fireEvents) {
			this.fire('objectAction',{
				action_name:'updateObject',
				attr_list:{'object':obj}
			});
		}   
	},

	replaceObject: function(objectId, data) {
		data.id = objectId;
		if ( this._objects.hasOwnProperty(objectId) ) {
			this._objects[objectId] = data;
			if (this._fireEvents) {
				this.fire('objectAction',{
					action_name:'updateObject',
					attr_list:{'objectId':this._objects[objectId]}
				});
			}            
		} else {
			console.log("[WARNING] L.Map.replaceObject objeto no existe, se llamara a L.Room.addObject");
			this.addObject(data);
		}
	},

	changeStore: function(store) {
		var old = this._objects;
		this._objects = store;
		if (this._fireEvents) {
			this.fire('objectAction',{
				action_name:'reloadObjects',
				attr_list:{}
			});
		}
		return this._objects;
	},

	eachObject: function(fn, context) {
		for (var key in this._objects) {
			if (this._objects.hasOwnProperty(key))
				fn.call(context, this._objects[key]);
		}
	},

	getObject: function(objId, FName, echoOff) {
		if (this.hasObject(objId)) {
			return this._objects[objId];
		} else {
			console.log('[WARNING] L.Map.'+FName+' no encontro objeto con id: ' + objId);
			return null;
		}
	},

	hasObject: function(objId){
		return this._objects.hasOwnProperty(objId);
	},

	getHistory: function(){
		return this._history;
	},

	getId: function(){
		return this.id;
	},

	getType: function(){
		return this.type;
	},

	getOwnerId: function() {
		return this._ownerId;
	},
	
	getRoomModerator: function() {
		return this._roomModerator;
	},

	setBackground: function(bg) {
		this.background = bg;
		this.fire('backgroundChange',this.background);
	},

	getBackground: function() {
		return this.background;
	},
	
	getRoomList: function() {
		return this._roomList;
	},
	
	setRoomList: function(roomList) {
		this._roomList = roomList;
		this.fire('roomListSet', this._roomList);
	},

	getBackgroundImageCss: function() {
		if ( this.background && this.background.uploadedFileId && this.background.uploadedFileName )
			return 'url(\'' + this.getBackgroundImageURL() + '\')';
		else
			return 'none';
	},

	getBackgroundImageURL: function() {
		if ( this.background && this.background.uploadedFileId && this.background.uploadedFileName )
			return Skt.basePath + '/files/' + this.background.uploadedFileId + '/' +  this.background.uploadedFileName;
		else
			return '';
	}

});